/*
 * TODO language 
 * Copyright (c) 2010 TODO Team
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */
    
#include <iostream>
#include <list>
#include <string>
#include <map>
#include "executor.h"
#include "opcodes.h"
#include "string.h"

namespace todo
{
/**
 * Get the variable value from map scope
 * @param name the variable name
 */
std::map<std::string, TodoValue*>::iterator TodoExecutor::get_var(const std::string& name)
	throw (todo::TodoException)
{
	std::map<std::string, TodoValue*>::iterator it;
	
	it = current_scope->vars.find(name);

	if (it == current_scope->vars.end()) {
		throw TodoException("The variable " + name + " wasn't declared!");
	}
	return it;
}

/**
 * Get the function from map scope
 * @param name the function name
 */
std::map<std::string, TodoFunction*>::iterator TodoExecutor::get_func(const std::string& name)
	throw (todo::TodoException)
{
	std::map<std::string, TodoFunction*>::iterator it;

	it = current_scope->funcs.find(name);
	
	if (it != current_scope->funcs.end()) {
		/* Found in current scope */
		return it;
	}
	if (current_scope != global_scope) {
		/* Try to find in the global scope */
		it = global_scope->funcs.find(name);
		if (it != global_scope->funcs.end()) {
			return it;
		}
	}
	throw TodoException("Couldn't found function " + name);
}

/**
 * Returns a pointer to the op value
 * @param op the operand
 */
TodoValue* TodoExecutor::get_val_ptr(TodoData& op) throw (todo::TodoException)
{
	if (op.type == TODO_VAR) {
		std::map<std::string, TodoValue*>::iterator var;	
		var = get_var(*op.data.val.str);
		
		return var->second;
	} else if (op.type == TODO_CONSTANT) {
		return &op.data;
	} else if (op.type == TODO_EXPR_VALUE) {
		return expr_value;
	} else if (op.type == TODO_RET_VALUE) {
		return ret_value;
	}
}

/**
 * Returns a new pointer to a temporary TodoValue
 */
TodoValue* TodoExecutor::get_new_expr_value(void)
{
	expr_value = new TodoValue;
	
	return expr_value;
}

// TODO_DECL_VAR
void TodoExecutor::declare_var(HANDLER_ARGS)
{
	std::pair<std::map<std::string, TodoValue*>::iterator, bool> ret;

	if (expr_value) {
		ret = current_scope->vars.insert(std::make_pair<std::string, TodoValue*>(*op1.data.val.str, expr_value));
	} else if (ret_value) {
		ret = current_scope->vars.insert(std::make_pair<std::string, TodoValue*>(*op1.data.val.str, ret_value));
	} else {
		if (op2.type == TODO_VAR) {
			std::map<std::string, TodoValue*>::iterator var;
			
			var = get_var(*op2.data.val.str);
			ret = current_scope->vars.insert(std::make_pair<std::string, TodoValue*>(*op1.data.val.str, var->second));
		} else {
			ret = current_scope->vars.insert(std::make_pair<std::string, TodoValue*>(*op1.data.val.str, &op2.data));
		}		
	}
	
	if (ret.second == false) {
		throw TodoException("Already exists variable named '" + *op1.data.val.str + "'");
	}
}

// TODO_CHG_VAR
void TodoExecutor::change_var(HANDLER_ARGS)
{
	std::map<std::string, TodoValue*>::iterator it;

	it = get_var(*op1.data.val.str);
	it->second = &op2.data;
}

// TODO_DIV
// TODO_DIV_ASSIGN
void TodoExecutor::div_assign(HANDLER_ARGS) throw (todo::TodoException)
{
	// Just resolve the expression
	if (opcode->type == TODO_DIV) {
		TodoValue *op1_val, *op2_val;
		
		op2_val = get_val_ptr(op2);
		
		if (op1.type != TODO_EXPR_VALUE) {
			op1_val = get_val_ptr(op1);
			expr_value = get_new_expr_value();
			expr_value->type = op1_val->type;
			expr_value->val = op1_val->val;
		}

		*expr_value /= op2_val;
	} else {
		// Resolve the expression assigning to a variable
		std::map<std::string, TodoValue*>::iterator var;
		var = get_var(*op1.data.val.str);

		if (op2.type == TODO_CONSTANT) {			
			*var->second /= &op2.data;
		} else if (op2.type == TODO_VAR) {
			std::map<std::string, TodoValue*>::iterator val;
				
			val = get_var(*op2.data.val.str);		
			*var->second /= val->second;
		}
	}
}

// TODO_MULT
// TODO_MULT_ASSIGN
void TodoExecutor::mult_assign(HANDLER_ARGS) throw (todo::TodoException)
{
	// Just resolve the expression
	if (opcode->type == TODO_MULT) {
		TodoValue *op1_val, *op2_val;
		
		op2_val = get_val_ptr(op2);
		
		if (op1.type != TODO_EXPR_VALUE) {
			op1_val = get_val_ptr(op1);
			expr_value = get_new_expr_value();
			expr_value->type = op1_val->type;
			expr_value->val = op1_val->val;
		}

		*expr_value *= op2_val;
	} else {
		// Resolve the expression assigning to a variable
		std::map<std::string, TodoValue*>::iterator var;
		var = get_var(*op1.data.val.str);

		if (op2.type == TODO_CONSTANT) {			
			*var->second *= &op2.data;
		} else if (op2.type == TODO_VAR) {
			std::map<std::string, TodoValue*>::iterator val;
				
			val = get_var(*op2.data.val.str);		
			*var->second *= val->second;
		}
	}
}

// TODO_ADD
// TODO_ADD_ASSIGN
void TodoExecutor::add_assign(HANDLER_ARGS) throw (todo::TodoException)
{
	// Just resolve the expression
	if (opcode->type == TODO_ADD) {
		TodoValue *op1_val, *op2_val;
		
		op2_val = get_val_ptr(op2);
		
		if (op1.type != TODO_EXPR_VALUE) {
			op1_val = get_val_ptr(op1);
			expr_value = get_new_expr_value();
			expr_value->type = op1_val->type;
			expr_value->val = op1_val->val;
		}
		
		*expr_value += op2_val;
	} else {
		// Resolve the expression assigning to a variable
		std::map<std::string, TodoValue*>::iterator var;
		var = get_var(*op1.data.val.str);

		if (op2.type == TODO_CONSTANT) {
			*var->second += &op2.data;
		} else if (op2.type == TODO_VAR) {
			std::map<std::string, TodoValue*>::iterator val;
				
			val = get_var(*op2.data.val.str);		
			*var->second += val->second;
		}
	}
}

// TODO_SUB
// TODO_SUB_ASSIGN
void TodoExecutor::sub_assign(HANDLER_ARGS) throw (todo::TodoException)
{
	// Just resolve the expression
	if (opcode->type == TODO_SUB) {
		TodoValue *op1_val, *op2_val;
		
		op2_val = get_val_ptr(op2);
		
		if (op1.type != TODO_EXPR_VALUE) {
			op1_val = get_val_ptr(op1);
			expr_value = get_new_expr_value();
			expr_value->type = op1_val->type;
			expr_value->val = op1_val->val;
		}
		
		*expr_value -= op2_val;
	} else {
		// Resolve the expression assigning to a variable
		std::map<std::string, TodoValue*>::iterator var;
		var = get_var(*op1.data.val.str);

		if (op2.type == TODO_CONSTANT) {			
			*var->second -= &op2.data;
		} else if (op2.type == TODO_VAR) {
			std::map<std::string, TodoValue*>::iterator val;
				
			val = get_var(*op2.data.val.str);		
			*var->second -= val->second;
		}
	}
}

// TODO_MOD
// TODO_MOD_ASSIGN
void TodoExecutor::mod_assign(HANDLER_ARGS) throw (todo::TodoException)
{
	// Just resolve the expression
	if (opcode->type == TODO_MOD) {
		TodoValue *op1_val, *op2_val;
		
		op2_val = get_val_ptr(op2);
		
		if (op1.type != TODO_EXPR_VALUE) {
			op1_val = get_val_ptr(op1);
			expr_value = get_new_expr_value();
			expr_value->type = op1_val->type;
			expr_value->val = op1_val->val;
		}
		
		*expr_value %= op2_val;
	} else {
		// Resolve the expression assigning to a variable
		std::map<std::string, TodoValue*>::iterator var;
		var = get_var(*op1.data.val.str);

		if (op2.type == TODO_CONSTANT) {			
			*var->second %= &op2.data;
		} else if (op2.type == TODO_VAR) {
			std::map<std::string, TodoValue*>::iterator val;
				
			val = get_var(*op2.data.val.str);		
			*var->second %= val->second;
		}
	}
}

// TODO_PRINTLN
void TodoExecutor::println(HANDLER_ARGS) throw (todo::TodoException)
{
	if (op1.type == TODO_VAR) {
		std::map<std::string, TodoValue*>::iterator it;
			
		it = get_var(*op1.data.val.str);
		std::cout << ops.get_value(*it->second) << std::endl;
	} else if (op1.type == TODO_RET_VALUE) {
		if (ret_value == NULL) {
			throw TodoException("ret_value is NULL");
		} else {
			std::cout << ops.get_value(*ret_value) << std::endl;
			ret_value = NULL;
		}
	} else if (op1.type == TODO_CONSTANT) {
		std::cout << ops.get_value(op1.data) << std::endl;
	} else if (op1.type == TODO_EXPR_VALUE) {
		std::cout << ops.get_value(*expr_value) << std::endl;
		delete expr_value;
		expr_value = NULL;
	}
}

// TODO_PRINT
void TodoExecutor::print(HANDLER_ARGS) throw (todo::TodoException)
{
	std::map<std::string, TodoValue*>::iterator it;

	if (op1.type == TODO_VAR) {
		it = get_var(*op1.data.val.str);		
		std::cout << ops.get_value(*it->second);
	} else if (op1.type == TODO_RET_VALUE) {
		std::cout << ops.get_value(*ret_value);
		ret_value = NULL;
	} else if (op1.type == TODO_CONSTANT) {
		std::cout << ops.get_value(op1.data);
	}
}

// TODO_DECL_FUNC
void TodoExecutor::declare_func(HANDLER_ARGS)
{
	std::pair<std::map<std::string, TodoFunction*>::iterator, bool> ret;

	ret = current_scope->funcs.insert(std::make_pair<std::string, TodoFunction*>(*op1.data.val.str, (TodoFunction*)opcode->info));
	
	if (ret.second == false) {
		throw TodoException("Cannot redeclare function '" + *op1.data.val.str + "'");
	}

	*op_num = ((TodoFunction*)opcode->info)->last_opcode;
}

// TODO_END_FUNC
void TodoExecutor::free_func(HANDLER_ARGS)
{
	// Go back to where the function was called
	*op_num = scopes.top()->calling_op_num;
	
	if (current_scope == global_scope) {
		std::cout << "Shouldn't happens!" << std::endl;
	} else {
		scopes.pop();
		delete current_scope;
		current_scope = scopes.top();
	}	
}

// TODO_FUNC_CALL
void TodoExecutor::func_call(HANDLER_ARGS)
{
	std::map<std::string, TodoFunction*>::iterator it;
	std::map<std::string, TodoType>::iterator arg;
	todo::todo_func_call *call_args;	
	TodoScope *scope;
	unsigned int i;	
	
	it = get_func(*op1.data.val.str);
	
	call_args = ((todo_func_call*)opcode->info);
	
	// Creating new scope
	scope = new TodoScope;
	
	// Save the calling op_num
	scope->calling_op_num = opcode->op_num;
	// Go to inside of function
	*op_num = it->second->op_num;
	
	if (it->second->args.size() != call_args->args.size()) {
		throw TodoException("Wrong param. count!");
	}
	
	arg = it->second->args.begin();	
	for (i = 0; i < call_args->args.size(); ++i) {
		scope->vars.insert(std::make_pair<std::string, TodoValue*>(arg->first, call_args->args[i]));		
		++arg;
	}
	scopes.push(scope);
	// Setting current scope
	current_scope = scopes.top();
}

// TODO_RETURN
void TodoExecutor::ret(HANDLER_ARGS)
{
	if (current_scope == global_scope) {
		// We are in global scope, just finalize the execution
		*op_num = ops.list.size();
	} else {
		if (op1.type == TODO_VAR) {
			std::map<std::string, TodoValue*>::iterator it;

			it = get_var(*op1.data.val.str);
			
			ret_value = it->second;
		} else if (op1.type == TODO_CONSTANT) {
			ret_value = &op1.data;
		} else if (op1.type == TODO_EXPR_VALUE) {
			ret_value = expr_value;
		}
	}
}

void TodoExecutor::method_call(HANDLER_ARGS)
{
	if (op1.type == TODO_CONSTANT) {
		switch (op1.data.type) {
			case TODO_STRING:
				break;
		}
	}
}

void TodoExecutor::if_stmt(HANDLER_ARGS)
{
	// std::cout << ">> if go to " << op2.data.val.tint << std::endl;
	
	if (op1.type == TODO_CONSTANT) {
		if (op1.data.val.tint == 0) {
			JMP_TO(op2.data.val.tint);
		}
	}
}

void TodoExecutor::elsif_stmt(HANDLER_ARGS)
{
	// std::cout << ">> elsif go to " << op2.data.val.tint << std::endl;

	// Check if it was jumped to here
	if (last_jmp != *op_num) {
		JMP_OUT(op2.data.val.tint-1);
	}
	
	if (op1.type == TODO_CONSTANT) {
		if (op1.data.val.tint == 0) {
			JMP_TO(op2.data.val.tint);
		}
	}	
}

void TodoExecutor::else_stmt(HANDLER_ARGS)
{
	// std::cout << ">> else go to " << op2.data.val.tint << std::endl;
	
	// Check if it was jumped to here
	if (last_jmp != *op_num) {
		JMP_TO(op2.data.val.tint);
	}
}

void TodoExecutor::endif_stmt(HANDLER_ARGS)
{

}

void TodoExecutor::init(void)
{	
	// Creating global scope
	todo::TodoScope* scope = new todo::TodoScope;
	scopes.push(scope);
	global_scope = current_scope = scopes.top();	
}

void TodoExecutor::run(void)
{
	unsigned int i;
	std::vector<todo::todo_opcode*>::size_type num_ops = ops.list.size();
	
	for (i = 0; i < num_ops; ++i) {
		try {
			if (ops.list[i]->handler) {
				(executor.*(ops.list[i]->handler))(&i, ops.list[i]->op1, ops.list[i]->op2, ops.list[i]);
			} else {
				std::cout << "Missing handler for opcode: " << ops.list[i]->type << std::endl;
			}
		} catch (TodoException e) {
			std::cout << "Fatal error: " << e.what() << std::endl;
			break;
		}
	}
	
	// Free the global_scope
	delete global_scope;
	// Free opcode data
	ops.free();
	// Free opcode list
	ops.list.clear();
}
} // todo
